var DEFINITION_FILE_EXT = 'ts'

var DEFINITION_INTERFACE_DEFINE = `
export abstract class Definition {
  initWithObject?(fields: any) {
    Object.keys(fields).forEach(key=> {
        let value = fields[key];
        this[key] = value;
      });
  }

  toObject?() {
    var obj = {};
    Object.keys(this).forEach(key=> {
      let value = this[key];
      obj[key] = value;
    });
    return obj;
  }
}
`

function definitionClassTmpl (CLASS_NAME, CLASS_REFS, PROPERTIES) {
  return `
import { Definition } from './Definition';
${CLASS_REFS}
export class ${CLASS_NAME} extends Definition {
  ${PROPERTIES}
}
  `
}

function definitionClassRefsTmpl (REFS) {
  return REFS.map((ref) => {
    if (!ref) return ''
    var list = ref.split('/')
    var className = list[list.length - 1]
    return `import type { ${className} } from './${ref}';\n`
  }).join('')
}

function definitionPropertiesTmpl (definition) {
  return `
${(() => {
      return Object.keys(definition.properties || {})
        .map((propertyName) => {
          var property = definition.properties[propertyName]
          var desc = property.description ? `\t/// ${property.description}\n` : ''
          if (property.$ref) {
            var className = definition.findRef(property.$ref).refClassName
            return `${desc}\t${propertyName}?: ${className};\n`
          }
          if (property.enum) {
            desc += `\t/// 枚举值: ${property.enum.toString()}\n`
          }
          switch (property.type) {
            case 'string':
              return `${desc}\t${propertyName}?: string;\n`
            case 'integer':
              return `${desc}\t${propertyName}?: number;\n`
            case 'float':
            case 'number':
              return `${desc}\t${propertyName}?: number;\n`
            case 'array':
              var className = property.items.$ref
                ? definition.findRef(property.items.$ref).refClassName
                : 'any'
              return `${desc}\t${propertyName}?: ${className}[];\n`
            default:
              return `${desc}\t${propertyName}?: any;\n`
          }
        })
        .join('')
    })()}
  `
}

function classTmpl (ClassName, ClassMethods, REFS) {
  return `/* eslint-disable @typescript-eslint/no-unused-vars */
import axios from '@/utils/http/axios'
${REFS}

export class ${ClassName} {
${ClassMethods}
}`
}

function methodTmpl (api) {
  var OperationId = api.operationId.replace(/\_\d+$/, '')
  var Summary = api.summary
  var Path = api.path
  var BaseUrl = api.basePath
  var Method = api.method
  var UpperMethod = Method.toUpperCase()
  var Host = api.host

  var paramsRegex = /(\{\w+\})/gm
  var params = Path.match(paramsRegex) || []
  var _url = BaseUrl + Path
  var pathUrl = BaseUrl + Path
  var paramsArgs = []
  for (var i = 0; i < params.length; i++) {
    var param = params[i].replace(/[\{\}]/g, '')
    _url = _url.replace(params[i], '${' + param + '}')
    paramsArgs.push(`${param}?: string, `)
  }

  var queryArgs = []
  var queryStrArray = []
  if (api.queryParams) {
    for (var i = 0; i < api.queryParams.length; i++) {
      var param = api.queryParams[i]
      var paramName = param.name
      queryStrArray.push(`${paramName}=\$\{${paramName} ?? "" \}`)
      switch (param.format) {
        case 'string':
          queryArgs.push(`${paramName}?: string, `)
          break
        case 'integer':
          queryArgs.push(`${paramName}?: number, `)
          break
        case 'float':
          queryArgs.push(`${paramName}?: number, `)
          break
        default:
          queryArgs.push(`${paramName}?: any, `)
          break
      }
    }
    if (queryStrArray.length > 0) {
      _url += '?' + queryStrArray.join('&')
    }
  }

  if (Method == 'post' || Method == 'put') {
    var ResponseParam = (api.parseResponseTs && api.parseResponseTs()) || 'any'
    var ParseBodyParam = api.parseBodyParamTs && api.parseBodyParamTs()
    return `
    static ${OperationId}Path: string = "${pathUrl}";
    /// ${Summary}
    static async ${OperationId}(${paramsArgs.join('')}${queryArgs.join('')}${ParseBodyParam ?? ''
      } headers?: any):  Promise<${ResponseParam}> {
      return axios.${Method}(${Host} + \`${_url}\`, ${!ParseBodyParam ? '{}' : 'data'},
      {
        headers
      });
    }
    `
  }

  if (Method == 'get' || Method == 'delete') {
    var ResponseParam = (api.parseResponseTs && api.parseResponseTs()) || 'any'
    return `
    static ${OperationId}Path: string = "${pathUrl}";

    static async ${OperationId}(${paramsArgs.join('')}${queryArgs.join(
      ''
    )} headers?: any): Promise<${ResponseParam}> {
      return axios.${Method}(${Host} + \`${_url}\`, {
        method: "${UpperMethod}",
        headers
      });
    }
    `
  }

  return ''
}

const nameMaps = {
  '接口返回对象': 'Result',
  '对象': 'VO',
}

function nameToCaml (str) {
  Object.keys(nameMaps).map((key) => {
    if (str.includes(key)) {
      str = str.replace(key, nameMaps[key])
    }
  })
  return str
}

module.exports = {
  DEFINITION_FILE_EXT: DEFINITION_FILE_EXT,
  DEFINITION_INTERFACE_DEFINE: DEFINITION_INTERFACE_DEFINE,
  definitionClassTmpl: definitionClassTmpl,
  definitionPropertiesTmpl: definitionPropertiesTmpl,
  definitionClassRefsTmpl: definitionClassRefsTmpl,
  shouldGenDefinition: true,
  classTmpl: classTmpl,
  methodTmpl: methodTmpl,
  nameToCaml: nameToCaml
}
